//////////////////////////////////////////////////////////////////////////
// $Id: Application.cpp,v 1.1 2008/12/20 12:20:36 sunjun Exp $
//////////////////////////////////////////////////////////////////////////

#include "Application.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

Application::Application()
{
	// load the plugins
	MOS::instance().loadPlugin("render");
	MOS::instance().loadPlugin("engine");

	Matrix m0,m1,m2,m3,m4;
	m1.setTranslate(100,200,300);
	m2.setScale(1.3,4.6,8.7);
	m0 = m1 * m2;
	Quaternion q1; q1.setRotate(Maths::degToRad(30.0f),Vector3(1,0,0));
	Quaternion q2; q2.setRotate(Maths::degToRad(45.0f),Vector3(0,1,0));
	Quaternion q3; q3.setRotate(Maths::degToRad(71.0f),Vector3(0,0,1));
	Quaternion q0 = q1*q2*q3;	
	m3.setRotate(q0);
	m0 = m3 * m2 * m1;
	m4.invert(m0);
	Vector4 v0(3.2f,4.3f,8.4f,6.5f);
	Vector4 v = m4.preMult(v0);

	I32 n = sizeof(Matrix);

	mFullscreen = FALSE;
	mWindowWidth = 800;
	mWindowHeight = 600;
	mColorBits = 32;
	mDepthBits = 16;
	mFrequency = 60;
	
	mFov = 90.0;
	mAspect = (F32)mWindowWidth / (F32)mWindowHeight;
	mZNear = 1.0;
	mZFar = 2000.0;

	mEye.set(-10,0,1000); 
	mCenter.set(0,0,0); 
	mUp.set(0,0,1);

	// create the window
	mWndPtr = MNEW(Window); CHECK(mWndPtr);
	mWndPtr->setText("launcher");
	mWndPtr->move( mWindowWidth, mWindowHeight, mFullscreen );

	// create the context
	mGraphPtr = MNEW(Graph); CHECK(mGraphPtr);
	mGraphPtr->bind( mWndPtr->handle() );	

	// create the render target
	{
		mRT0Ptr = MNEW(RenderTexture(mGraphPtr.ptr())); CHECK(mRT0Ptr);
		{
			std::vector<U8>data(16);
			U8*ptr = &data[0];
			// set the the type
			(*((U32*)ptr)) = Texture::TT_TEXTURE;	ptr += sizeof(U32);
			// set the the format : D3DFMT_A8R8G8B8
			(*((U32*)ptr)) = 21;					ptr += sizeof(U32);
			// set the the width
			(*((U32*)ptr)) = mWindowWidth;			ptr += sizeof(U32);
			// set the the height
			(*((U32*)ptr)) = mWindowHeight;			ptr += sizeof(U32);
			mRT0Ptr->load(&data[0],data.size());
		}

		mRT1Ptr = MNEW(RenderTexture(mGraphPtr.ptr())); CHECK(mRT1Ptr);
		{
			std::vector<U8>data(16);
			U8*ptr = &data[0];
			// set the the type
			(*((U32*)ptr)) = Texture::TT_TEXTURE;	ptr += sizeof(U32);
			// set the the format : D3DFMT_R32F
			(*((U32*)ptr)) = 114;					ptr += sizeof(U32);
			// set the the width
			(*((U32*)ptr)) = mWindowWidth;			ptr += sizeof(U32);
			// set the the height
			(*((U32*)ptr)) = mWindowHeight;			ptr += sizeof(U32);
			mRT1Ptr->load(&data[0],data.size());
		}
		
		mDSPtr = MNEW(DepthSurface(mGraphPtr.ptr())); CHECK(mDSPtr);
		{
			std::vector<U8>data(16);
			U8*ptr = &data[0];
			// set the the format : D3DFMT_D16
			(*((U32*)ptr)) = 80;					ptr += sizeof(U32);
			// set the the width
			(*((U32*)ptr)) = mWindowWidth;			ptr += sizeof(U32);
			// set the the height
			(*((U32*)ptr)) = mWindowHeight;			ptr += sizeof(U32);
			mDSPtr->load(&data[0],data.size());
		}
	}

	// create the scene primitive
	{
		mPrimitivePtr = MNEW(Primitive); CHECK(mPrimitivePtr);

		// set the effect		
		EffectPtr effect_ptr = MNEW(Effect(mGraphPtr.ptr())); CHECK(effect_ptr);
		{
			MFile file;
			CHECK(file.open("effects/scene.fx"));
			U32 pos = file.tell();
			file.seek(0,2);
			U32 size = file.tell();
			file.seek(pos,0);
			MDataPtr data_ptr = MNEW(MData);
			CHECK(data_ptr);
			data_ptr->resize(size);
			CHECK(file.read(data_ptr.ptr())==size);
			file.close();
			effect_ptr->load(data_ptr->get(), data_ptr->size());
		}
		mPrimitivePtr->setEffect(effect_ptr.ptr());

		// set the constant
		ConstantPtr constant_ptr = MNEW(Constant); CHECK( constant_ptr );
		constant_ptr->setTexture(dynamic_cast<Texture*>(mRT0Ptr.ptr()));			
		mPrimitivePtr->setConstant( "gBaseTex", constant_ptr.ptr() );

		// set the vertex format
		std::vector<VertexFormat::FORMAT>vertex_formats;
		VertexFormat::FORMAT format; 
		format.stream = 0;
		format.offset = 0;
		format.type = VertexFormat::VET_FLOAT4;
		format.semantic = VertexFormat::VES_POSITION;
		vertex_formats.push_back(format);
		format.offset = 16;
		format.type = VertexFormat::VET_FLOAT2;
		format.semantic = VertexFormat::VES_TEXCOORD;
		vertex_formats.push_back(format);
		format.stream = 0xFF;				
		vertex_formats.push_back(format);
		VertexFormatPtr vf_ptr = MNEW(VertexFormat(mGraphPtr.ptr())); CHECK(vf_ptr);
		vf_ptr->load(&vertex_formats[0]);
		mPrimitivePtr->setVertexFormat(vf_ptr.ptr());

		// set the vertex buffer for the primitive
		const F32 vertices[] = 
		{
			-1.000000, -1.000000, 0.000000,  1.000000,  0.000000,  1.000000,
			1.0000000, -1.000000, 0.000000,  1.000000,  1.000000,  1.000000,
			1.0000000, 1.0000000, 0.000000,  1.000000,  1.000000,  0.000000,
			-1.000000, 1.0000000, 0.000000,  1.000000,  0.000000,  0.000000,
		};
		VertexBufferPtr vb_ptr = MNEW(VertexBuffer(mGraphPtr.ptr())); CHECK(vb_ptr);
		vb_ptr->load(&vertices[0],sizeof(vertices),24);
		mPrimitivePtr->setVertexBuffer(vb_ptr.ptr(), 0);

		// set the index buffer for the primitive
		const U16 indexes[] = 
		{
			3, 0, 2,
			2, 0, 1,
		};
		IndexBufferPtr ib_ptr = MNEW(IndexBuffer(mGraphPtr.ptr())); CHECK(ib_ptr);
		ib_ptr->load(&indexes[0],sizeof(indexes)/sizeof(U16),sizeof(U16));
		mPrimitivePtr->setIndexBuffer(ib_ptr.ptr());

		// locate the primitive
		mPrimitivePtr->locate(0,4,0);
	}

	{
		ConstantPtr constant_ptr = MNEW(Constant); CHECK( constant_ptr );
		constant_ptr->setMatrix(Matrix());
		mWVPPtr = MNEW(Dict::Key("gWVP", constant_ptr.ptr()));
	}

	// load the dds file
	{
		FILE * file = fopen( "textures/default.dds", "rb" ); CHECK(file);
		U32 pos = ftell( file );
		fseek( file, 0, SEEK_END );
		U32 size = ftell( file );
		fseek( file, pos, SEEK_SET );
		std::vector<U8>data(size);
		CHECK( fread( &data[0], 1, size, file ) == size );
		fclose( file );	

		TexturePtr tex_ptr = MNEW(Texture(mGraphPtr.ptr())); CHECK(tex_ptr);
		tex_ptr->load(&data[0],size);
	}

	// load the fx file
	{
		FILE * file = fopen( "effects/default.fx", "rb" ); CHECK(file);
		U32 pos = ftell( file );
		fseek( file, 0, SEEK_END );
		U32 size = ftell( file );
		fseek( file, pos, SEEK_SET );
		std::vector<U8>data(size);
		CHECK( fread( &data[0], 1, size, file ) == size );
		fclose( file );	

		EffectPtr effect_ptr = MNEW(Effect(mGraphPtr.ptr())); CHECK(effect_ptr);
		effect_ptr->load(&data[0],size);

		HANDLE technique = effect_ptr->getTechnique("T0");		
		CHECK(technique);
		HANDLE pass = effect_ptr->getPass(technique,"P0");
		U32 count = effect_ptr->begin();
		for (I32 i = 0; i < count; i++) 
		{
			effect_ptr->beginPass(i);			
			effect_ptr->endPass();
		}
		effect_ptr->end();
	}

	// read the mesh file
	MFile file;
	CHECK(file.open( "models/mesh.xml" ));
	U32 pos = file.tell();
	file.seek(0,2);
	U32 size = file.tell();
	file.seek(pos,0);
	MDataPtr data_ptr = MNEW(MData);
	CHECK(data_ptr);
	data_ptr->resize(size);
	CHECK(file.read(data_ptr.ptr())==size);
	file.close();

	mMeshPtr = MNEW(Mesh); CHECK(mMeshPtr);
	mMeshPtr->bind( mGraphPtr.ptr() );
	mMeshPtr->load( data_ptr.ptr() );
}

Application::~Application()
{
	if( mMeshPtr ) mMeshPtr.release();

	// release the rc 
	if( mGraphPtr ) { mGraphPtr->bind( NULL ); mGraphPtr.release(); }
	
	// release the window
	if( mWndPtr ) mWndPtr.release();

	// free all of the plugin
	MOS::instance().freePlugin("engine");
	MOS::instance().freePlugin("render");	
}

//////////////////////////////////////////////////////////////////////////

/*
====================
getSystemTime
====================
*/
INLINE U32 CDECL getSystemTime()
{
	static U32 starttime = 0;
	U32 now = timeGetTime();	
	if( !starttime ) { starttime = now; return 0; }
	return now - starttime;
}

/*
====================
getSystemFPS
====================
*/
INLINE U32 CDECL getSystemFPS( U32 time )
{
	static U32 last_time = 0;	
	static U32 frames = 0;
	static U32 fps = 0;	
	frames++;	
	U32 frame_time = time - last_time;
	if( frame_time > 1000 ) 
	{
		fps = frames * 1000 / frame_time;
		last_time = time;
		frames = 0;
	}
	return fps;
}

/*
====================
run
====================
*/
VOID Application::run()
{
	// get system fps
	U32	system_time = getSystemTime();	
	U32 fps = getSystemFPS( system_time );
	static U32 last_time = 0;
	U32 frameTime = system_time - last_time;
	if( system_time - last_time > 1000 ) 
	{
		last_time = system_time;
		Str text = VA( "launcher (fps : %d)", fps );
		mWndPtr->setText(text.c_str());
	}

	// begin the scene
	ScenePtr begin_scene = MNEW(Scene(Scene::ST_BEGIN));
	mGraphPtr->exec( begin_scene.ptr() );

	// the view
	ViewPtr view_ptr = MNEW(View); CHECK(view_ptr);

	// set the render target
	TargetPtr target0 = MNEW(Target);
	target0->setRenderTexture(mRT0Ptr.ptr());
	target0->setRenderTexture(mRT1Ptr.ptr());
	target0->setDepthSurface(mDSPtr.ptr());
	mGraphPtr->exec(target0.ptr());
	{
		// set the viewport
		ViewportPtr viewport = MNEW(Viewport);
		viewport->set(0,0,mWindowWidth,mWindowHeight,0,1);
		mGraphPtr->exec( viewport.ptr() );

		// clear the target and the zbuffer
		ClearPtr clear = MNEW(Clear);
		clear->set(Clear::CT_TARGET|Clear::CT_ZBUFFER,0x00000000,1.0,0);
		mGraphPtr->exec( clear.ptr() );	

		// make the projection and the view
		Matrix proj_mat = Matrix::makePerspective(mFov,mAspect,mZNear,mZFar);
		Matrix view_mat = Matrix::makeLookAt(mEye,mCenter,mUp);
		Matrix world_view_proj_mat = view_mat * proj_mat;
		dynamic_cast<Constant*>(mWVPPtr->ptr())->setMatrix(world_view_proj_mat);

		// draw the mesh
		mMeshPtr->draw(view_ptr.ptr());
	}

	// reset the default render target
	TargetPtr target1 = MNEW(Target);	
	mGraphPtr->exec(target1.ptr());	
	{
		// set the viewport
		ViewportPtr viewport = MNEW(Viewport);
		viewport->set(0,0,mWindowWidth,mWindowHeight,0,1);
		mGraphPtr->exec( viewport.ptr() );

		// clear the target and the zbuffer
		ClearPtr clear = MNEW(Clear);
		clear->set(Clear::CT_TARGET|Clear::CT_ZBUFFER,0xff0000ff,1.0,0);
		mGraphPtr->exec( clear.ptr() );

		mGraphPtr->exec(mPrimitivePtr.ptr());
	}

	// end the scene
	ScenePtr end_scene = MNEW(Scene(Scene::ST_END));
	mGraphPtr->exec( end_scene.ptr() );

	// swap the operations to the render thread
	mGraphPtr->swap();
}

//////////////////////////////////////////////////////////////////////////